#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include <debug.h>
#include <os_api.h>
#include <device_api.h>
#include <hal/uart.h>
#include <net/mqttclient.h>

#define UART_TASK_STACK_SIZE (10 * 1024)
#define TASK_STACK_SIZE (20 * 1024)
static char _uart_task_stack[UART_TASK_STACK_SIZE];
static char _task_stack[TASK_STACK_SIZE];

#define APP_MSG_INIT 1
#define APP_MSG_MQTT_CONNECT 2
#define APP_MSG_MQTT_DATA 3
#define APP_MSG_UART_DATA 4

#define DEV_CLIENT_ID_LEN 128
#define DEV_HOSTNAME_LEN 128
#define DEV_PORT_LEN 16
#define DEV_TOPIC_LEN 240

typedef struct app_msg_s
{
    uint32_t msg_id;
    uint32_t data_len;
    void *data;
} app_msg_t;

typedef struct app_data_s
{
    OSTaskRef uart_task_ref;
    OSMsgQRef uart_msgq;
    OSTimerRef uart_timer;
    UINT32 uart_ft; //unit: ms

    OSTaskRef task_ref;
    OSMsgQRef cmdq;
    OSTimerRef timer;

    mqtt_client_t *mqtt_client;

    CHAR client_id[DEV_CLIENT_ID_LEN + 1];
    CHAR host[DEV_HOSTNAME_LEN + 1]; /*xx.xx.xx.xx:port, or URL*/
    CHAR port[DEV_PORT_LEN + 1];
    CHAR sub_topic[DEV_TOPIC_LEN + 1];
    CHAR pub_topic[DEV_TOPIC_LEN + 1];

    UINT8 serial_buf[1024];
    UINT32 serial_buf_sz;
} app_data_t;

static int app_send_msg(app_data_t *ad, uint32_t msg_id, uint32_t data_len, void *data)
{
    app_msg_t msg;

    memset(&msg, 0, sizeof(msg));
    msg.msg_id = msg_id;
    msg.data_len = data_len;
    msg.data = data;
    if (OS_SUCCESS == OSAMsgQSend(ad->cmdq, sizeof(msg), (UINT8 *)&msg, OS_NO_SUSPEND))
    {
        return 0;
    }
    return -1;
}

static void _sub_topic_handle(void *client, message_data_t *msg)
{
    mqtt_client_t *c = client;
    app_data_t *ad = c->mqtt_reconnect_data;
    int ret;

    char *mqtt_payload = NULL;

    mqtt_payload = malloc(msg->message->payloadlen + 1);
    if (!mqtt_payload)
        return;

    memcpy(mqtt_payload, msg->message->payload, msg->message->payloadlen);
    mqtt_payload[msg->message->payloadlen] = '\0';

    ret = app_send_msg(ad, APP_MSG_MQTT_DATA, msg->message->payloadlen, mqtt_payload);
    if (ret < 0)
    {
        LOG_PRINTF("%s: error queue full", __func__);
        free(mqtt_payload);
    }
}

static void app_mqtt_pub(app_data_t *ad, char *data, uint32_t data_len)
{
    int ret;
    mqtt_message_t msg;

    LOG_PRINTF("%s: +\n", __func__);
    if (ad->mqtt_client != NULL && ad->mqtt_client->mqtt_client_state == CLIENT_STATE_CONNECTED)
    {
        memset(&msg, 0, sizeof(msg));
        msg.qos = QOS0;
        msg.payloadlen = data_len;
        msg.payload = data;
        ret = mqtt_publish(ad->mqtt_client, ad->pub_topic, &msg);
        if (ret == KAWAII_MQTT_SUCCESS_ERROR)
        {
            LOG_PRINTF("%s: pub OK\n", __func__);
        }
    }

    LOG_PRINTF("%s: -\n", __func__);
}

static void app_handle_mqtt_data(app_data_t *ad, char *data, uint32_t data_len)
{
    LOG_PRINTF("%s: +", __func__);

    hal_UartWrite(UART_PORT_3, (UINT8 *)data, data_len);
    free(data);

    LOG_PRINTF("%s: -", __func__);
}

static void app_mqtt_connect_tmr_cb(UINT32 param)
{
    app_data_t *ad = (app_data_t *)param;

    app_send_msg(ad, APP_MSG_MQTT_CONNECT, 0, NULL);
}

static void app_mqtt_connect(app_data_t *ad)
{
    int rc;

    LOG_PRINTF("%s: +", __func__);

    if (ad->mqtt_client == NULL)
    {
        ad->mqtt_client = mqtt_lease();
        if (ad->mqtt_client == NULL)
        {
            LOG_PRINTF("%s() mqtt_lease fail", __FUNCTION__);
            return;
        }

        ad->mqtt_client->mqtt_reconnect_data = ad;
        ad->mqtt_client->mqtt_host = ad->host;
        ad->mqtt_client->mqtt_port = ad->port;
        ad->mqtt_client->mqtt_client_id = ad->client_id;
        ad->mqtt_client->mqtt_clean_session = 1;
    }

    rc = mqtt_connect(ad->mqtt_client);
    if (rc != KAWAII_MQTT_SUCCESS_ERROR)
    {
        LOG_PRINTF("%s() mqtt_connect fail:%d", __FUNCTION__, rc);
        OSATimerStart(ad->timer, SEC2TICKS(30), 0, app_mqtt_connect_tmr_cb, (UINT32)ad);
        return;
    }

    mqtt_subscribe(ad->mqtt_client, ad->sub_topic, QOS0, _sub_topic_handle);
    if (rc != KAWAII_MQTT_SUCCESS_ERROR)
    {
        LOG_PRINTF("%s() mqtt_subscribe fail:%d", __FUNCTION__, rc);
    }

    LOG_PRINTF("%s: -", __func__);
}

static void app_handle_uart_data(app_data_t *ad, char *data, uint32_t data_len)
{
    LOG_PRINTF("%s: +", __func__);

    app_mqtt_pub(ad, data, data_len);
//    free(data);

    LOG_PRINTF("%s: -", __func__);
}

static void app_uart_timeout_cb(UINT32 param)
{
    app_data_t *ad = (app_data_t *)param;
    int ret;

    ret = app_send_msg(ad, APP_MSG_UART_DATA, ad->serial_buf_sz, ad->serial_buf);
    if (ret < 0)
    {
        LOG_PRINTF("%s: error queue full", __func__);
    }
    ad->serial_buf_sz = 0;
}

static void app_uart_task(void *param)
{
    app_data_t *ad = param;
    UartRxMsg_Type msg;
    int ret;

    while (1)
    {
        memset(&msg, 0, sizeof(msg));
        ret = OSAMsgQRecv(ad->uart_msgq, (UINT8 *)&msg, sizeof(msg), OS_SUSPEND);
        ASSERT(ret == OS_SUCCESS);

        if (sizeof(ad->serial_buf) >= ad->serial_buf_sz + msg.length)
        {
            memcpy(ad->serial_buf + ad->serial_buf_sz, msg.data, msg.length);
            ad->serial_buf_sz += msg.length;
        }
        free(msg.data);

        OSATimerStop(ad->uart_timer);
        ret = OSATimerStart(ad->uart_timer, MS2TICKS(ad->uart_ft), 0, app_uart_timeout_cb, (UINT32)ad);
        ASSERT(ret == OS_SUCCESS);
    }
}

static void app_init_timer_cb(UINT32 param)
{
    app_data_t *ad = (app_data_t *)param;

    if (dev_IsProductionMode())
        return;

    app_send_msg(ad, APP_MSG_INIT, 0, NULL);
}

static void app_init(app_data_t *ad)
{
    LOG_PRINTF("%s: +", __func__);

    strncpy(ad->client_id, "asr-mqtt-test", sizeof(ad->client_id));
    strncpy(ad->host, "iot.yuge-info.com", sizeof(ad->host));
    strncpy(ad->port, "1883", sizeof(ad->port));
    strncpy(ad->sub_topic, "/energy/bmsResponse", sizeof(ad->sub_topic));
    strncpy(ad->pub_topic, "/energy/bms", sizeof(ad->pub_topic));

    ad->uart_ft = 100;
    hal_UartOpen(UART_PORT_3, 0, ad->uart_msgq);
    OSATimerStart(ad->timer, SEC2TICKS(10), 0, app_mqtt_connect_tmr_cb, (UINT32)ad);

    LOG_PRINTF("%s: -", __func__);
}

static void app_task(void *param)
{
    app_data_t *ad = (app_data_t *)param;
    int ret;
    app_msg_t msg;

    while (1)
    {
        memset(&msg, 0, sizeof(msg));
        ret = OSAMsgQRecv(ad->cmdq, (UINT8 *)&msg, sizeof(msg), OS_SUSPEND);
        ASSERT(ret == OS_SUCCESS);
        switch (msg.msg_id)
        {
        case APP_MSG_INIT:
            app_init(ad);
            break;
        case APP_MSG_MQTT_CONNECT:
            app_mqtt_connect(ad);
            break;
        case APP_MSG_MQTT_DATA:
            app_handle_mqtt_data(ad, msg.data, msg.data_len);
            break;
        case APP_MSG_UART_DATA:
            app_handle_uart_data(ad, msg.data, msg.data_len);
            break;
        default:
            break;
        }
    }
}

int main()
{
    int ret;
    app_data_t *ad;

    dev_SetAppVersion("DTU-V1.0");

    ad = malloc(sizeof(*ad));
    ASSERT(ad);
    memset(ad, 0, sizeof(*ad));

    ret = OSAMsgQCreate(&ad->uart_msgq, "uartRxMsgQ", sizeof(UartRxMsg_Type), 100, OS_FIFO);
    ASSERT(ret == OS_SUCCESS);
    ret = OSATaskCreate(&ad->uart_task_ref, _uart_task_stack, UART_TASK_STACK_SIZE, OS_APP_PRIORITY - 1, "appUart", app_uart_task, ad);
    ASSERT(ret == OS_SUCCESS);

    ret = OSAMsgQCreate(&ad->cmdq, "app", sizeof(app_msg_t), 10, OS_FIFO);
    ASSERT(ret == OS_SUCCESS);
    ret = OSATaskCreate(&ad->task_ref, _task_stack, TASK_STACK_SIZE, OS_APP_PRIORITY, "app", app_task, ad);
    ASSERT(ret == OS_SUCCESS);

    ret = OSATimerCreate(&ad->uart_timer);
    ASSERT(ret == OS_SUCCESS);
    ret = OSATimerCreate(&ad->timer);
    ASSERT(ret == OS_SUCCESS);

    ret = OSATimerStart(ad->timer, SEC2TICKS(10), 0, app_init_timer_cb, (UINT32)ad);
    ASSERT(ret == OS_SUCCESS);
}
